#ifdef __C51__
#define static
code
#endif
#include "atlas.xbm"
#ifdef __C51__
#undef static
#endif

bit get_atlas_pixel(uint8_t x, uint8_t y) {
	uint16_t i = (uint16_t) y * atlas_width + x;
	return (atlas_bits[i >> 3] >> (i & 7)) & 1;
}

xdata uint16_t lcd_g_scanline[8];
xdata char lcd_t_buffer[4][16];

#define lcd_t_prepare_line(i, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15) do { \
	dm.t.t[i][0] = a0; dm.t.t[i][1] = a1; dm.t.t[i][2] = a2; dm.t.t[i][3] = a3; \
	dm.t.t[i][4] = a4; dm.t.t[i][5] = a5; dm.t.t[i][6] = a6; dm.t.t[i][7] = a7; \
	dm.t.t[i][8] = a8; dm.t.t[i][9] = a9; dm.t.t[i][10] = a10; dm.t.t[i][11] = a11; \
	dm.t.t[i][12] = a12; dm.t.t[i][13] = a13; dm.t.t[i][14] = a14; dm.t.t[i][15] = a15; \
} while (0)
#define lcd_t_prepare_line_string(i, s) \
	draw_text_line(i, s[0], s[1], s[2], s[3], s[4], s[5], s[6], s[7], s[8], s[9], s[10], s[11], s[12], s[13], s[14], s[15])

void lcd_g_prepare_scanline(uint16_t x) {
	register uint8_t i;
	for (i = 0; i < 8; i++) {
		dm.g.scanline[i] = x;
	}
}

void draw_part(
	uint8_t dest_x, // 0...128
	uint8_t src_x, // 0...128
	uint8_t src_y, // 0...192; 255 = fill
	uint8_t w // 1..128
) {
	bit b;
	while (w) {
		if (src_y == 255) {
			b = 1;
		} else {
			b = get_atlas_pixel(src_x, src_y);
		}
		dm.g.scanline[dest_x >> 4] |= 1 << (dest_x & 15);
		if (!b) dm.g.scanline[dest_x >> 4] ^= 1 << (dest_x & 15);
		dest_x++;
		src_x++;
		w--;
	}
}

// generate the line by parsing drawing_part
void draw_scanline(uint8_t y) {
	register uint8_t i;
	lcd_g_set_word(0, y, dm.g.scanline[0]);
	for (i = 1; i < 8; i++) {
		lcd_g_set_next_word(dm.g.scanline[i]);
	}
}

void draw_texture(
	uint8_t dest_x, uint8_t dest_y,
	uint8_t src_x, uint8_t src_y, uint8_t w, uint8_t h
) {
	uint8_t x, y;
	for (x = 0; x < w; x++) {
		for (y = 0; y < h; y++) {
			lcd_g_set_pixel(dest_x + x, dest_y + y, 0, get_atlas_pixel(src_x + x, src_y + y));
		}
	}
}

void draw_char(uint8_t x, uint8_t y, char c) {
	draw_texture(
		x, y,
		((uint8_t) c & 15) * 6, (((uint8_t) c >> 4) - 2) * 12, 6, 12
	);
}

void draw_text(uint8_t x, uint8_t y, const char* s) {
	register uint8_t tx = x;
	while (*s) {
		if (*s == '\n') {
			tx = x;
			y += 12;
		} else {
			draw_char(tx, y, *s);
			tx += 6;
		}
		s++;
	}
}

void alter_word_by_whole_set(uint8_t x, uint8_t y) {
	lcd_g_set_word(x, y, 0xffff);
}

void alter_word_by_whole_clear(uint8_t x, uint8_t y) {
	lcd_g_set_word(x, y, 0);
}

void alter_word_by_whole_invert(uint8_t x, uint8_t y) {
	lcd_g_set_word(x, y, ~lcd_g_get_word(x, y));
}

void alter_word_by_mask_set(uint8_t x, uint8_t y, uint16_t mask) {
	mask |= lcd_g_get_word(x, y);
	lcd_g_set_word(x, y, mask);
}

void alter_word_by_mask_clear(uint8_t x, uint8_t y, uint16_t mask) {
	mask = ~mask;
	mask &= lcd_g_get_word(x, y);
	lcd_g_set_word(x, y, mask);
}

void alter_word_by_mask_invert(uint8_t x, uint8_t y, uint16_t mask) {
	mask ^= lcd_g_get_word(x, y);
	lcd_g_set_word(x, y, mask);
}

void fill_rect(uint8_t x, uint8_t y, uint8_t w, uint8_t h, bit and_mask, bit xor_mask) {
	uint8_t i, j, w1, x1;
	uint16_t mask;
	void (*alter_word_by_whole)(uint8_t, uint8_t);
	void (*alter_word_by_mask)(uint8_t, uint8_t, uint16_t);
	if (and_mask) {
		if (xor_mask) {
			alter_word_by_whole = alter_word_by_whole_invert;
			alter_word_by_mask = alter_word_by_mask_invert;
		} else {
			return;
		}
	} else {
		if (xor_mask) {
			alter_word_by_whole = alter_word_by_whole_set;
			alter_word_by_mask = alter_word_by_mask_set;
		} else {
			alter_word_by_whole = alter_word_by_whole_clear;
			alter_word_by_mask = alter_word_by_mask_clear;
		}
	}
	// test the alignment on word boundaries
	if (w <= (~x & 15)) {
		// less than 16 pixels wide and fitted in one word
		x1 = x >> 4;
		mask = ((1 << w) - 1) << (x & 15);
		for (j = y; j < y + h; j++) alter_word_by_mask(x1, j, mask);
		return;
	}
	if ((w1 = x & 15)) {
		// left not aligned
		x1 = x >> 4;
		mask = ~((1 << w1) - 1);
		for (j = y; j < y + h; j++) alter_word_by_mask(x1, j, mask);
		w -= 16 - w1;
		x |= 15;
		x++;
	}
	// fill whole words
	x1 = x >> 4;
	w1 = w >> 4;
	for (i = x1; i < x1 + w1; i++) {
		for (j = y; j < y + h; j++) {
			alter_word_by_whole(i, j);
		}
	}
	x += w1 << 4;
	w &= 15;
	if (w) {
		// right not aligned
		x1 = x >> 4;
		mask = ((1 << w) - 1);
		for (j = y; j < y + h; j++) alter_word_by_mask(x1, j, mask);
	}
}

void draw_dialog(void) {
	draw_text_line_string(0, "\xa9\xb0\xa9\xa4\xa9\xa4\xa9\xa4\xa9\xa4\xa9\xa4\xa9\xa4\xa9\xb6");
	dm.
	draw_text_line_string(3, "\xa9\xb9\xa9\xa5\xa9\xa5\xa9\xa5\xa9\xa5\xa9\xa5\xa9\xa5\xa9\xbe");
}
